<?php


namespace app\community\boot\oauth2;


use app\community\oauth2\Entities\UserEntity;
use app\community\oauth2\Repository\{
    AccessTokenRepository,
    AuthCodeRepository,
    ClientRepository,
    RefreshTokenRepository,
    ScopeRepository,
    UserRepository
};
use DateInterval;
use League\OAuth2\Server\{
    CryptKey,
    AuthorizationServer
};
use iflow\annotation\interfaces\BootInterface;
use iflow\annotation\utils\Boot;
use iflow\Container\implement\annotation\tools\data\Inject;
use League\OAuth2\Server\Entities\UserEntityInterface;
use League\OAuth2\Server\Exception\OAuthServerException;
use League\OAuth2\Server\Grant\{
    AbstractGrant,
    AuthCodeGrant
};
use League\OAuth2\Server\RequestTypes\AuthorizationRequest;
use Psr\Http\Message\ServerRequestInterface;

#[Boot]
class oauthBoot implements BootInterface {

    #[Inject]
    protected AccessTokenRepository $accessTokenRepository;
    #[Inject]
    protected ScopeRepository $scopeRepository;
    #[Inject]
    protected ClientRepository $clientRepository;
    #[Inject]
    protected AuthCodeRepository $authCodeRepository;
    #[Inject]
    protected RefreshTokenRepository $refreshTokenRepository;
    #[Inject]
    protected UserRepository $userRepository;

    protected AuthorizationServer $oauthServer;
    protected AbstractGrant $authGrant;
    protected AuthorizationRequest $authServerRequest;

    protected UserEntity $userEntity;

    protected array $oauthConfig = [];

    /**
     * @throws \Exception
     */
    public function boot(): bootInterface
    {
        // TODO: Implement boot() method.
        $config = config('oauth') ?: [];
        // 当未开启 OAUTH2服务时
        if (!$config['enable']) return $this;

        $this->oauthConfig = $config['oauth2'][$config['default']] ?? [];

        $this->userEntity = new UserEntity();
        return $this -> initAuthorizationServer();
    }

    /**
     * 初始化Oauth2Server
     * @return bootInterface
     * @throws \Exception
     */
    public function initAuthorizationServer(): bootInterface {
        $this->oauthServer = new AuthorizationServer(
            $this->clientRepository,
            $this->accessTokenRepository,
            $this->scopeRepository,
            new CryptKey($this->oauthConfig['openSslCert']['private'], keyPermissionsCheck: false),
            $this->oauthConfig['openSslCert']['encryptionKey']
        );
        return $this -> setAuthGrant();
    }

    /**
     * 设置用户授权类型
     * @param string $duration
     * @param string $refreshDuration
     * @return static
     * @throws \Exception
     */
    public function setAuthGrant(string $duration = 'PT10M', string $refreshDuration = 'P1M'): static
    {
        $this->authGrant = new AuthCodeGrant(
            $this->authCodeRepository,
            $this->refreshTokenRepository,
            new \DateInterval($duration)
        );

        $this->authGrant -> setRefreshTokenTTL(
            new \DateInterval($refreshDuration)
        );

        return $this;
    }

    /**
     * 启用授权类型
     * @param AbstractGrant|null $authorizeGrant
     * @param DateInterval|bool|null $refreshTokenTTL
     * @param DateInterval|null $accessTokenTTL
     * @return static
     */
    public function enableGrantType(
        AbstractGrant $authorizeGrant = null,
        DateInterval|bool $refreshTokenTTL = null,
        DateInterval $accessTokenTTL = null
    ): static {
        if ($authorizeGrant && $refreshTokenTTL !== false) {
            $authorizeGrant -> setRefreshTokenTTL(
                $refreshTokenTTL ?: new DateInterval('P1M')
            );
        }
        $this->oauthServer -> enableGrantType($authorizeGrant ?: $this->authGrant, $accessTokenTTL);
        return $this;
    }

    /**
     * 验证请求
     * @param ServerRequestInterface|null $serverRequest
     * @return static
     * @throws OAuthServerException
     */
    public function validateAuthorizationRequest(?ServerRequestInterface $serverRequest = null): static
    {
        $serverRequest = $serverRequest ?: request() -> getServerRequestPsr7();
        $this->authServerRequest = $this->oauthServer
            -> validateAuthorizationRequest($serverRequest);
        return $this->setUserEntity();
    }

    /**
     * 设置用户实体
     * @return static
     * @param ?UserEntityInterface $userEntity
     */
    public function setUserEntity(?UserEntityInterface $userEntity = null): static
    {
        $this -> authServerRequest
            -> setUser(
                $userEntity ?: $this->userEntity
            );
        return $this;
    }

    /**
     * @return AuthorizationRequest
     */
    public function getAuthorizationRequest(): AuthorizationRequest
    {
        return $this->authServerRequest;
    }

    /**
     * @return AuthorizationServer
     */
    public function getOauthServer(): AuthorizationServer
    {
        return $this->oauthServer;
    }

    /**
     * @return UserRepository
     */
    public function getUserRepository(): UserRepository
    {
        return $this->userRepository;
    }

    /**
     * @return RefreshTokenRepository
     */
    public function getRefreshTokenRepository(): RefreshTokenRepository
    {
        return $this->refreshTokenRepository;
    }

}